Repository navigation
Update module github.com/labstack/echo/v4 to v4.16.1 [SECURITY] - #85
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
Contributor
Author
ℹ️ Artifact update noticeFile name: go.modIn order to perform the update(s) described in the table above, Renovate ran the
Details:
|
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| Security | 12 medium 3 minor 23 high |
🟢 Metrics 0 complexity · 0 duplication
Metric Results Complexity 0 Duplication 0
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
renovate
Bot
deleted the
renovate/go-github.com-labstack-echo-v4-vulnerability
branch
August 30, 2026 21:39
renovate
Bot
force-pushed
the
renovate/go-github.com-labstack-echo-v4-vulnerability
branch
2 times, most recently
from
August 31, 2026 01:02
d4b5a57 to
b4af300
Compare
renovate
Bot
force-pushed
the
renovate/go-github.com-labstack-echo-v4-vulnerability
branch
2 times, most recently
from
August 31, 2026 17:49
b4af300 to
3489cb4
Compare
renovate
Bot
force-pushed
the
renovate/go-github.com-labstack-echo-v4-vulnerability
branch
from
October 28, 2026 20:07
3489cb4 to
2a18235
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
v4.15.0→v4.16.1Echo: Encoded slash (%2F) bypasses route-level protection and exposes static files
CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq
More information
Details
Summary
Echo's router and static file handler disagree on URL path decoding. The router matches routes using the raw encoded path (preserving
%2Fas-is), whileStaticDirectoryHandlerunescapes%2Fto/before resolving filesystem paths. This allows an attacker to bypass route-level access controls and read static files without authorization.Details
Root cause 1 —
router.golines 798-802:The router uses
req.URL.RawPathfor route matching whenuseEscapedPathForRoutingis false (the default). This means/admin%2Fsecret.txtis treated as a single path segment and does NOT match the/admin/*route pattern.Root cause 2 —
echo.golines 559-568:StaticDirectoryHandlercallsurl.PathUnescape()on the path parameter before opening files. This converts%2Fback to/, resolvingadmin/secret.txton disk.PoC (Screenshot)
Sample:

403:

Bypass with encoded slash:

Impact
Unauthorized static file disclosure. Applications that protect route prefixes with authentication middleware while also serving static files from a broader root are vulnerable. An attacker only needs to encode the slash (
/→%2F) in the URL to bypass all route-level protection.Common affected pattern:
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo: WebSocket proxy can retain connections and handlers after client disconnect
CVE-2026-118262 / GHSA-9969-6676-49h5
More information
Details
Impact
Echo's Proxy middleware could retain a handler, copy goroutines and network connections after a WebSocket client disconnected if the upstream kept its connection open. The raw proxy waited for both copy directions to finish without reliably closing the other connection when one direction ended. Rejected upgrade responses with HTTP keep-alive were one way to trigger this behavior.
Repeated connections can accumulate resources and cause denial of service. The impact depends on the proxy route's accessibility, upstream behavior and configured connection deadlines. This is a medium-severity resource-retention issue; no confidentiality or integrity impact is claimed.
Affected versions and patch
Affected versions are v4.15.0 through v4.16.0 and v5.0.0 through v5.4.0. The regression was introduced by commit e2133320c7, merged in #2852.
Upgrade to v4.16.1 or v5.4.1. The patch uses Go's HTTP reverse proxy, preserves ordinary HTTP handling for rejected upgrades, and closes both sides of a WebSocket tunnel when either direction ends. It also covers an upstream that receives client EOF but deliberately keeps its side open.
Fix commits: v4, v5.
Workarounds
Disable WebSocket proxy routes until upgrading, or apply bounded connection lifetimes and resource limits at the frontend and upstream. Server idle timeouts alone may not cover hijacked WebSocket connections.
Credits
Reported by @irisitymichaelgrundberg.
Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo: BodyDump captures request and response bodies without size limits in v4
CVE-2026-118260 / GHSA-f48p-c9v4-h7xv
More information
Details
Impact
Echo v4's opt-in BodyDump middleware read complete request bodies into memory and retained complete response bodies without a capture limit. A client able to submit large requests to a route using BodyDump could cause large allocations before the application handler ran. Large responses, including responses whose size can be influenced by a client, also caused an additional unbounded capture allocation.
Repeated or concurrent requests can exhaust process memory and cause denial of service. BodyDump is not enabled by default. Deployments with effective request limits may reduce the request-side exposure; response capture requires separate consideration.
Affected versions and patch
Echo v4 versions through v4.16.0 are affected. Upgrade to v4.16.1.
The patch adds
BodyDumpConfig.MaxRequestBytesandMaxResponseBytes, each defaulting to 5 MiB when zero. A value of-1explicitly restores unlimited capture. The complete request remains readable by the handler and the complete response is delivered to the client; only the callback's captured data is bounded. Request close behavior and read errors are preserved.Fix commit.
Workarounds
Disable BodyDump on exposed routes, particularly large uploads and downloads. Alternatively, enforce body-size limits before BodyDump and independently bound the responses that the application generates.
Credits
Reported by @q490107865. The patch also incorporates request-body preservation work from @jakezwang in #3105.
Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo: Decompress permits unbounded gzip expansion in v4
CVE-2026-118261 / GHSA-h7wr-284f-5qvc
More information
Details
Impact
Echo v4's opt-in Decompress middleware exposed a gzip-decoded request stream without limiting its expanded size. A small compressed request could therefore produce a very large decoded body. Handlers that read or bind that body could consume excessive memory or CPU, and repeated or concurrent requests could cause denial of service.
The middleware is not enabled by default. A limit on the compressed request size alone does not bound the decompressed size. Applications that already apply an effective body limit after decompression have reduced exposure.
Affected versions and patch
Echo v4 versions through v4.16.0 are affected. Upgrade to v4.16.1.
The patch adds
DecompressConfig.MaxDecompressedSize, defaulting to 100 MiB when zero. A value of-1explicitly disables the limit. Reading beyond the cap returnsErrStatusRequestEntityTooLarge(HTTP 413), including through JSON and XML binding. Bodies exactly at the configured limit remain valid. Streaming enforcement requires callers to handle body-read errors.Fix commit.
Workarounds
Disable gzip request decompression on exposed routes, or place BodyLimit after Decompress so the application limit applies to decoded bytes. Apply suitable request deadlines and concurrency limits as well.
Credits
Reported by @q490107865. The exact-limit behavior incorporates @Shubham-Padkonde's work in #3136.
Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo: Rejected WebSocket upgrades can bypass Echo route-level access controls
CVE-2026-118264 / GHSA-cxf3-9h37-7rr8
More information
Details
Impact
Echo's Proxy middleware selected a raw tunnel when the client requested a WebSocket upgrade, before validating whether the configured upstream accepted it. If the upstream returned an ordinary HTTP response and kept the connection alive, subsequent HTTP requests on that connection were forwarded directly to the same upstream without passing through Echo's router or route-specific middleware.
This can bypass authentication or authorization enforced only at Echo's routes. Exploitation requires an accessible proxy route, an upstream that rejects the upgrade without closing the connection, and a target resource on that upstream without independent authorization. The issue does not provide arbitrary-host proxying or bypass the upstream's own authentication.
Affected versions and patch
The raw proxy implementation is present throughout the v4 and v5 release lines up to v4.16.0 and v5.4.0. Runtime reproductions confirmed v4.15.0, v4.16.0, v5.0.0 and v5.4.0; source history also shows the missing upgrade-response validation in v4.0.0.
Upgrade to v4.16.1 or v5.4.1. The patch routes WebSocket upgrades through Go's HTTP reverse proxy and enters a tunnel only after a valid upstream upgrade response. Rejected upgrades remain ordinary HTTP requests.
Fix commits: v4, v5.
Workarounds
Reject WebSocket upgrade requests before they reach affected Echo proxy routes, or enforce authorization independently at every upstream resource. Merely returning a non-101 response from the upstream does not prevent the vulnerable raw tunnel.
Credits
Reported by @Duodum.
Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo has CSRF Protection Bypass via MethodOverride Rewriting POST to Safe Method
CVE-2026-118266 / GHSA-r7w9-592q-9vg4
More information
Details
Summary
The
MethodOverridemiddleware accepts any non-empty string as the override target method — including safe methods (GET, HEAD, OPTIONS, TRACE) — when rewriting a POST request. WhenMethodOverrideis registered beforeCSRFin the post-routing middleware chain (e.Use()), it rewrites the request method to a safe method beforeCSRFevaluates it. The CSRF middleware then skips token validation for the now-safe method, allowing state-changing POST handlers to execute without any CSRF check. Both the legacy token path and the modernSec-Fetch-Siteprotection path are bypassed simultaneously.Affected Component
middleware/method_override.go—MethodOverrideConfig.ToMiddleware()(lines 55–71): no whitelist on the override target methodmiddleware/csrf.go—CSRFConfig.ToMiddleware()(lines 188–189): safe-method bypass branch reads the already-rewritten methodmiddleware/csrf.go—checkSecFetchSiteRequest()(line 283):Sec-Fetch-Sitesafe check also reads the rewritten methodDescription
MethodOverride accepts safe methods as override targets
MethodOverrideConfig.ToMiddleware()(method_override.go:55-71) restricts the source method (only POST requests are overrideable) but applies no whitelist to the target:The docstring's security claim — "For security reasons, only POST method can be overridden" — refers only to the source-method restriction. There is no guard preventing POST from being rewritten to
GET,HEAD,OPTIONS, orTRACE. The test suite (method_override_test.go) only tests POST→DELETE and POST→PUT; POST→GET is never tested or blocked.CSRF safe-method branch reads the already-rewritten method
CSRFConfig.ToMiddleware()(csrf.go:188-189) makes its validation decision based onc.Request().Methodat the time it runs:If MethodOverride has already run and set
req.Method = "GET", thisswitchfalls into the skip branch regardless of the original HTTP method or what handler was matched.The
Sec-Fetch-Site-based path incheckSecFetchSiteRequest()(csrf.go:283) is identically vulnerable:With
req.Method = "GET"(already rewritten),isSafe = trueand the function returnstrue— the request is allowed even whenSec-Fetch-Siteiscross-site. Both CSRF protection paths are bypassed in a single step.Execution chain
In
echo.go:700, routing and middleware wrapping are performed in one step:e.router.Route(c)evaluates withreq.Method = "POST"→ matches and binds the POST/transferhandlerapplyMiddlewarewraps it:MethodOverride → CSRF → postHandlerAt request execution time:
3. MethodOverride runs:
req.Method == "POST"→ reads form field_method="GET"→req.Method = "GET"→ callsnext4. CSRF runs:
c.Request().Method="GET"→case http.MethodGet→ skips token validation → callsnext5. postHandler runs (the handler matched in step 1 — the original POST
/transferstate-change handler)The state-changing handler executes with no CSRF token validation and with no
Sec-Fetch-Sitecheck.The missing target-method whitelist is the root cause
The legitimate use case for MethodOverride is allowing HTML forms (which only support GET/POST) to send PUT, DELETE, or PATCH requests. There is no legitimate scenario in which an HTML form needs to rewrite POST to GET — that can always be expressed as an ordinary GET request. Allowing POST→GET (and HEAD/OPTIONS/TRACE) serves no valid purpose and is inherently dangerous when any safe-method-aware security middleware (CSRF, logging, audit) appears downstream.
Proof of Concept
The same bypass works with
MethodFromHeader(X-HTTP-Method-Override: GET) andMethodFromQuery(?_method=GET).Impact
CSRF()is reachable without a valid CSRF token when the vulnerable middleware ordering is presentSec-Fetch-Sitemodern-browser CSRF protection is bypassed simultaneously — browsers that supportSec-Fetch-Siteprovide no additional defenseGETas the HTTP method for what were actually state-changing operationsRecommended Remediation
Option 1: Whitelist only unsafe methods as override targets in
MethodOverride(preferred)The root cause is that MethodOverride accepts safe methods as targets. Restricting the override to legitimately overrideable methods (PUT, PATCH, DELETE) closes the bypass regardless of middleware ordering:
Option 2: Use
e.Pre()forMethodOverrideand document the ordering requirementPlacing MethodOverride in
e.Pre()(pre-routing middleware) causes the router to match the overridden method, not the original POST. If only POST/transferis registered, a POST-to-GET override results in a 405/404 — the handler never executes. Add an explicit documentation warning that MethodOverride must not be placed ine.Use()when CSRF middleware is also registered:Option 1 is strongly preferred as it closes the vulnerability at the source and is safe regardless of middleware placement.
Credit
This vulnerability was discovered and reported by bugbunny.ai.
Patches
Fixed in
github.com/labstack/echo/v5v5.4.0 andgithub.com/labstack/echo/v4v4.16.0. The MethodOverride middleware no longer overrides a POST toGET,HEAD,OPTIONS,TRACEorCONNECT.Workarounds
Register MethodOverride with
Echo#Pre(as documented), register CSRF before MethodOverride, or use the default header getter (X-HTTP-Method-Override), which a cross-site form cannot set.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo's middleware.Static:
%2F-encoded path bypasses route/group middleware guarding a statically-served pathCVE-2026-118650 / GHSA-375p-5qhx-8wq4
More information
Details
Summary
When
middleware.Staticis mounted globally (e.Use(middleware.Static(...))) and an application relies on a route- or group-level middleware guard to restrict a path that also falls under the served static root, a%2F-encoded request bypasses that guard while the file is still served. This is the same encoding/decoding mismatch fixed forStaticFS/StaticDirectoryHandlerin CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq, but the fix did not cover themiddleware.Staticpath. In addition, theStaticConfig.EnablePathUnescapingdocstring states the default configuration prevents exactly this bypass, which is inaccurate for the global-mount case.This is not path traversal: no file outside the static root is accessed. The served file lives inside the static root; the issue is that access-control expectations set by a route guard are inconsistent between the literal and
%2F-encoded forms of the same path.Details
Root cause — the router and the static handler disagree on how to interpret
%2F:GET /admin%2Fsecret.txtdoes not match a/admin/...route or group; any middleware attached to that route/group is therefore skipped.middleware.Staticselects its path source inmiddleware/static.go(~L219): with a globale.Usemount there is no wildcard route, so it usesc.Request().URL.Path, whichnet/httphas already decoded (%2F→/). It thenpath.Cleans (~L235) andOpens (~L259)admin/secret.txt, serving the file.Net effect: for the literal path the guard runs; for the
%2F-encoded path the guard is skipped but the same file is served.Relevant code:
middleware/static.go~L219 (p := c.Request().URL.Path), ~L235 (path.Clean), ~L259 (currentFS.Open)StaticConfig.EnablePathUnescapingdocstring (static.go~L60-67) — claims the default prevents the/admin%2fprivate.txt→/admin/*guard bypass; this is not true for the global mount.Relationship to prior advisories:
%2Frouter-vs-handler mismatch, but fixed forStaticFS/StaticDirectoryHandler. Themiddleware.Staticpath was not covered — this report is that gap.PoC
Environment:
golang:1.25, module using the official release (go get github.com/labstack/echo/v5@v5.3.1, no localreplace). Static rootpublic/contains a marker filepublic/admin/secret.txt=SECRET.Minimal app (the pattern under test: static served globally + a group guard intended to protect
/admin):Requests (unauthenticated):
Observed on
v5.3.1: literal →401;%2F→200with the file contents. For contrast, serving via the (patched)e.StaticFS("/")returns404for the same%2Frequest, showing themiddleware.Staticpath was not covered by the CVE-2026-55677 fix.Impact
An unauthenticated remote user can retrieve a file served by
middleware.Staticeven when the application attached a route/group middleware guard to that path, by substituting/with%2F. The consequence is limited to files already inside the served static root (CWE-863 authorization inconsistency; CWE-436 interpretation conflict) — there is no access to files outside the static root.Honest caveat on validity / severity: this only has security impact for applications that (a) serve a directory statically to the public and (b) place a sensitive file inside that same directory while relying on a route-level guard to protect it. That combination is arguably a misconfiguration — a statically-served directory is public by design — so maintainers may reasonably classify this as a documentation fix (correct the
EnablePathUnescapingclaim) or won't-fix rather than a code vulnerability. It is filed primarily because (1) the router-vs-handler%2Finconsistency is the exact class fixed in CVE-2026-55677 but left uncovered formiddleware.Static, and (2) the docstring currently promises protection it does not provide.Patches
Fixed in
github.com/labstack/echo/v5v5.4.0 andgithub.com/labstack/echo/v4v4.16.0. With the default settings, the Static middleware resolves files from the same form of the path that the router matched, so percent-encoded paths such as/admin%2Fsecret.txtor/%61dmin/secret.txtno longer reach a file under a guarded route. Paths with.,..or empty segments are no longer served.Note: middleware registered with
Echo#Useruns before route and group middleware, so route guards never protect files served by a global Static middleware. Keep protected files outside its root directory, or serve them withEcho#Staticbehind the guard.Workarounds
Keep files that need protection outside the Static middleware's root directory.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo vulnerable to Reflected Cross-Site Scripting (XSS) via unsanitized JSONP callback in Context.JSONP / Context.JSONPBlob
CVE-2026-118652 / GHSA-h9g5-28mm-hx3g
More information
Details
Summary
The
Context.JSONP,Context.JSONPBlob, and internaljsonPBlobhelper incontext.godirectly concatenate the caller-suppliedcallbackparameter into a JavaScript response without any validation, escaping, or allow-listing.This creates a high-impact reflected XSS that can be triggered by any route passing attacker-controlled data (query string, form, header, path param, etc.) into the callback.
Present in current master and v5.1.0 (latest release).
Vulnerable Code (verbatim)
No sanitization is performed on the callback string.
Exploit (one-request repro)
Any route using the official pattern is vulnerable:
Attacker request:
Server response:
→ Immediate arbitrary JavaScript execution (full reflected XSS).
Impact
Affected Versions
Workarounds
Manually sanitize the callback before calling JSONP/JSONPBlob:
Or migrate away from JSONP entirely (use CORS + application/json).
References
Patches
Fixed in
github.com/labstack/echo/v5v5.4.0 andgithub.com/labstack/echo/v4v4.16.0.Context.JSONPandContext.JSONPBlobaccept only a callback that is empty, a JavaScript identifier or a dot-separated path of identifiers (ASCII letters, digits,_and$); any other callback returns a 400 Bad Request error wrappingErrInvalidJSONPCallbackand nothing is written. JSONP responses also carryX-Content-Type-Options: nosniff.Workarounds
Validate the callback in your handler before calling
Context.JSONP, for example against^[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*$, and setX-Content-Type-Options: nosniff. JSONP responses can be read by any website with the user's cookies, so do not serve data that requires authentication through JSONP.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo has Open Redirect via Control-Character Prefix Defeating sanitizeURI
CVE-2026-118656 / GHSA-v753-g4cw-jm48
More information
Details
Summary
Echo's dedicated open-redirect guard,
sanitizeURI, decides whether a redirect target is a local path by inspecting exactly two byte positions:uri[0]anduri[1]. If the value begins with a control character such as a tab or a newline, followed by a separator pair, the guard does not fire and the value is written to theLocationheader unchanged.Browsers remove tab and newline characters from URLs before parsing them, and treat a backslash as a forward slash for HTTP and HTTPS URLs. A
Locationvalue of/\t/attacker.exampleis therefore resolved by the browser as an absolute URL onattacker.example. Confirmed end to end in a headless browser.Technical background
The guard
middleware/slash.go:144(duplicated verbatim atecho.go:958):The condition is anchored at absolute offsets 0 and 1. It cannot see a separator that has been shifted by one byte.
Data flow
RemoveTrailingSlash(middleware/slash.go:112-140) readsurl.Path, whichnet/httphas already percent-decoded, strips the trailing slash, and appends the raw query string:AddTrailingSlashdoes the same in the opposite direction atmiddleware/slash.go:67.A request for
/%09/attacker.example/x/hasurl.Pathequal to"/\t/attacker.example/x/"(a literal TAB byte).sanitizeURIevaluatesuri[0] == '\t', the condition is false, and the value passes through untouched.Context.Redirect(context.go:735) writes it to theLocationheader. Go's header writer removes CR and LF but leaves TAB, which is all that is required.Conditional analysis
uri[0]anduri[1]are separators. Trust boundary: server-side sanitization of attacker-influenced path data.uri[1]being a separator) is never reached because the first condition (uri[0]being a separator) already failed.Location.Note that the newline variant does not survive:
net/httpreplaces CR and LF in header values. TAB does survive, and TAB alone is sufficient because the browser strips it.Proof of concept
Attached PoC:
ECHO-2026-002_poc.zip(in this directory). Extract it and followECHO-2026-002/README.mdfor the full walkthrough. The archive contains aself-contained Go module (
go.mod,go.sum,main.go,README.md).ECHO-2026-002_poc.zip
Quick start:
Environment used for verification
github.com/labstack/echo/v5)The vulnerable configuration
Reproduction
In-process check. The program requests five path shapes and applies the browser
URL-parsing steps that matter (strip TAB, LF, CR; treat a backslash as a slash; a
leading
//starts an authority):GOFLAGS=-mod=mod go run .^Iis a literal TAB byte. The last two rows are deliberate negatives, includedso the mechanism (the guard reads only
uri[0]anduri[1]) is unambiguous.Over the wire, with the
-servevariant running on127.0.0.1:18002:Locationon the wire. No backslash is needed for this variant:cat -Arenders the tab as^I, confirming it is a literal byte in the header.byte offsets rather than generic header injection:
open this URL in a browser:
Observed in headless Chromium:
The browser left the victim origin entirely, so this is a working open redirect,
not merely a suspicious header.
Exit status
0means the finding reproduced;1means an expectation wasviolated.
Impact
An attacker can craft a link on the victim domain that redirects the victim to an arbitrary external site. Practical consequences:
The client must follow the redirect (user interaction is required), which is why this is scored Medium rather than High, but open redirects on authentication-adjacent origins routinely escalate in practice.
Recommended fixes
Normalize before deciding, instead of probing two fixed offsets.
Additional hardening:
Locationvalue inContext.Redirectso every caller benefits, instead of relying on each call site to callsanitizeURI.%09/,%09%5C,%0A/) against both trailing-slash middlewares. There is currently no test that assertssanitizeURIbehavior at all.Patches
Fixed in
github.com/labstack/echo/v5v5.4.0 andgithub.com/labstack/echo/v4v4.16.0. The trailing slash middlewares and the static directory redirect percent-encode control characters in the redirect path before collapsing leading slashes, so a browser can no longer be sent to another host.Workarounds
Use the trailing slash middlewares without
RedirectCode(the default rewrites the path internally instead of redirecting), or reject request paths that contain control characters before these middlewares.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo has Arbitrary Scheme Injection in WWWRedirect/NonWWWRedirect Middlewares via X-Forwarded-Proto
CVE-2026-118658 / GHSA-23wf-92cp-8pcj
More information
Details
Summary
The
c.Scheme()method in Echo (context.go:162-181) trusts theX-Forwarded-Proto,X-Forwarded-Protocol, andX-Url-Schemerequest headers without validating that the value ishttporhttps. TheWWWRedirect()andNonWWWRedirect()middlewares use this unsanitized scheme to construct redirect URLs, allowing an attacker to inject arbitrary URI schemes into theLocationresponse header.Affected Components
context.go:162-181-Scheme()method returns attacker-controlled value verbatimmiddleware/redirect.go:172-184-redirectWWW/redirectNonWWWuse unsanitized schemeProof of Concept
Confirmed injectable schemes:
javascript,data,vbscript,file,ftp,ssh,telnet,gopher.Only
WWWRedirect()andNonWWWRedirect()(andWithConfigvariants) are affected.HTTPSRedirect,HTTPSWWWRedirect,HTTPSNonWWWRedirectare NOT affected (they hardcodehttps://).Impact
X-Forwarded-Protofile://scheme injection could trigger file access in some browser configurationsSuggested Fix
Whitelist scheme in
c.Scheme()to only returnhttporhttps:Disclosure
Source code review, manually verified with actual HTTP server. Tested on Echo v5.0.4 (latest master).
Patches
Fixed in
github.com/labstack/echo/v5v5.1.1 andgithub.com/labstack/echo/v4v4.15.2.Context.Scheme()now accepts onlyhttp,https,wsorwssfrom theX-Forwarded-Proto,X-Forwarded-ProtocolandX-Url-Schemeheaders, and falls back tohttpfor any other value (#2953).Workarounds
Upgrade to a patched version. If you cannot upgrade, have your edge proxy overwrite or remove the
X-Forwarded-Proto,X-Forwarded-ProtocolandX-Url-Schemerequest headers.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Echo: HTTPS Redirect and HSTS Bypass via Unconditional Trust of Forwarded Scheme Headers
CVE-2026-118657 / GHSA-2ffq-g2xg-c22p
More information
Details
Description
Context.Scheme()unconditionally trusts four client-controlled HTTP headers to determine the request scheme, with no mechanism to configure trusted sources. This allows a remote unauthenticated attacker to bypass HTTPS redirect enforcement and suppress HSTS headers.The trusted headers (checked in this order of precedence):
X-Forwarded-ProtoX-Forwarded-ProtocolX-Forwarded-SslX-Url-SchemeThreat model
Scenario A — Directly exposed application (vulnerable by default):
The Echo application listens on HTTP and is reachable directly by clients (no L7 proxy in front), or an attacker can bypass the proxy and reach the backend directly. In this case, the attacker has full control over all request headers. Sending
X-Forwarded-Proto: httpsdefeats theHTTPSRedirectmiddleware entirely — traffic stays on plaintext HTTP, enabling man-in-the-middle attacks.This is the primary scenario. The application is vulnerable by default with no misconfiguration required — simply using
e.Pre(middleware.HTTPSRedirect())as documented is sufficient.Scenario B — Behind L7 proxy with header sanitization (reduced risk):
If the application is strictly behind a correctly configured L7 proxy that always overwrites
X-Forwarded-Protoand there is no direct access to the backend, the practical risk is lower. However, this relies on perimeter controls external to the framework, not on the framework itself. As Echo's own IP-extraction documentation states: "you must not trust HTTP headers unconditionally."Root cause
context.go, lines 161–181:Two additional issues with this implementation:
No value validation.
Scheme()returns any arbitrary string from the header, not just"http"or"https". This breaks the documented contract ("returns the HTTP protocol scheme,httporhttps") and causes unexpected behavior in middleware that uses the return value to construct URLs (e.g.,WWWRedirectbuildsscheme + "://www." + host + uri).Consumed by security-critical middleware. The return value is used directly by redirect middleware (
middleware/redirect.go:136) a